#include "sys_uart.h"

#define ENABLE_JOYSTICK
#define ENABLE_MOUSE
#define MOUSE_SLOWDOWN

// Payload data format(8 bytes)

// 57 AB 88 0B 10: Keyboard
// MM KK KK KK KK KK KK KK

// Multimedia
// 57 AB 88 13 30 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 07
// 57 AB 88 06 30 02 00 00 02 04

// 57 AB 88 0B 00: Joy stick
// 7F 7F 80 80 80 8F 00 00 // Y
// 7F 7F 80 80 80 4F 00 00 // B
// 7F 7F 80 80 80 2F 00 00 // A
// 7F 7F 80 80 80 1F 00 00 // X
// 7F 7F 80 80 80 0F 20 00 // START
// 7F 7F 80 80 80 0F 10 00 // SELECT
// 7F 7F 80 80 80 0F 01 00 // L
// 7F 7F 80 80 80 0F 02 00 // R
// 7F FF 80 80 80 0F 00 00 // UP
// FF 00 80 80 80 0F 00 00 // DOWN
// 00 7F 80 80 80 0F 00 00 // LEFT
// FF 7F 80 80 80 0F 00 00 // RIGHT

// 57 AB 88 0B 20: Mouse
// 01 01 00 00 00 00 00 00 // l-button
// 01 02 00 00 00 00 00 00 // r-button
// 01 04 00 00 00 00 00 00 // m-button
// 01 00 00 00 00 00 01 00 // wheel up
// 01 00 00 00 00 00 FF 00 // wheel down
// 01 00 05 00 00 00 00 00 // right
// 01 00 F4 FF 00 00 00 00 // left
// 01 00 06 00 17 00 00 00 // down
// 01 00 00 00 E6 FF 00 00 // up

// 57 AB 88 0A 20: Mouse
// 01 01 00 00 00 00 00 // l-button
// 01 02 00 00 00 00 00 // r-button
// 01 04 00 00 00 00 00 // m-button
// 01 00 00 00 00 01 00 // wheel up
// 01 00 00 00 00 FF 00 // wheel down
// 01 00 05 00 00 00 00 // right
// 01 00 F4 0F 00 00 00 // left
// 01 00 06 70 01 00 00 // down
// 01 00 00 60 FE 00 00 // up

// 57 AB 88 09 20: Mouse
// 01 00 00 00 00 00 // l-button
// 02 00 00 00 00 00 // r-button
// 04 00 00 00 00 00 // m-button
// 00 00 00 00 00 01 // wheel up
// 00 00 00 00 00 FF // wheel down
// 00 05 00 00 00 00 // right
// 00 F4 FF 00 00 00 // left
// 00 06 00 17 00 00 // down
// 00 00 00 E6 FF 00 // up

// 57 AB 88 08 20: Mouse
// 01 01 00 00 00 // l-button
// 01 02 00 00 00 // r-button
// 01 04 00 00 00 // m-button
// 01 00 00 00 01 // wheel up
// 01 00 00 00 FF // wheel down
// 01 00 05 00 00 // right
// 01 00 F4 00 00 // left
// 01 00 06 17 00 // down
// 01 00 00 E6 00 // up

// 57 AB 88 07 20: Mouse
// 01 00 00 00 // l-button
// 02 00 00 00 // r-button
// 04 00 00 00 // m-button
// 00 00 00 01 // wheel up
// 00 00 00 FF // wheel down
// 00 05 00 00 // right
// 00 F4 00 00 // left
// 00 06 17 00 // down
// 00 00 E6 00 // up

// 57 AB
//       82 Ax
//       88 0B 11 [00 00 00 00 00 00 00 00] 17 17

// Joy stick
//       88 0B 01 [LR UD 80 80 80 xF xx 00] ss cc

#define STATE_IDLE       0
#define STATE_REQ        1 // 82 Ax
#define STATE_HID_LEN    2 // 1 byte
#define STATE_DEV_ID     3 // 1 byte
#define STATE_KBD_DATA   4 // 8 bytes
#define STATE_KBD_SEQ    5 // 1 byte
#define STATE_KBD_SUM    6 // 1 byte
#define STATE_KBD_JUNK   7 // 0 byte
#define STATE_JOY_DATA   8 // 8 bytes
#define STATE_JOY_SEQ    9 // 1 byte
#define STATE_JOY_SUM   10 // 1 byte
#define STATE_MOU_DATA  11 // 8 bytes
#define STATE_MOU_SEQ   12 // 1 byte
#define STATE_MOU_SUM   13 // 1 byte
#define STATE_DUMP_DAT  14
#define STATE_MM_DAT    15 // Multimedia

static int ch9350_status;

// https://deskthority.net/wiki/Scancode
// total 256 bits
unsigned char key_bitmap[32];
unsigned char key_bitmap_new[32];
unsigned char kbd_led_ctl;

static unsigned int  preamble; // 57 AB 8x
static unsigned char length;   // 0B
static unsigned char id;       // 11
static unsigned char seq_no;
static unsigned char payload_cnt;
static unsigned char checksum;

static unsigned char state_frame[11];

static int phantom;
static int mouse_present;

#ifdef ENABLE_JOYSTICK
// Right, Left, Down, Up, Start, Select, B, A

int joy1_status;
int joy2_status;

static unsigned char joy_status_old;
static unsigned char joy_status_new;

//assign joy_map1 = joy1_status;
//assign joy_map2 = joy2_status;
#endif

#ifdef ENABLE_MOUSE
unsigned char mouse_key;
unsigned char mouse_key_new;

signed short  mouse_delta_x;
signed short  mouse_delta_x_new;
signed short  mouse_delta_x_gui;

signed short  mouse_delta_y;
signed short  mouse_delta_y_new;
signed short  mouse_delta_y_gui; // for GUI using

signed char   mouse_wheel;
signed char   mouse_wheel_new;
#endif

#ifdef HID_DEBUG_MODE
unsigned char hid_raw_data[2][256];
#endif

extern int emu_mini_kbd; // defined in desktop.c

void ch9350_init()
{
	payload_cnt   = 0;
	phantom       = 0;
	preamble      = 0;
	ch9350_status = STATE_IDLE;
	memset(key_bitmap, 0, sizeof(key_bitmap));
#ifdef ENABLE_JOYSTICK
	joy1_status    = 0;
	joy2_status    = 0;
	joy_status_old = 0;
	joy_status_new = 0;
#endif

	mouse_present = 0;

#ifdef ENABLE_MOUSE
	mouse_key     = 0;
	mouse_delta_x = 0;
	mouse_delta_y = 0;
	mouse_wheel   = 0;
#endif

	mouse_delta_x_gui = 0;
	mouse_delta_y_gui = 0;

	kbd_led_ctl = 0;

	state_frame[ 0] = 0x57;
	state_frame[ 1] = 0xAB;
	state_frame[ 2] = 0x12;
	state_frame[ 3] = 0x00;
	state_frame[ 4] = 0x00;
	state_frame[ 5] = 0x00;
	state_frame[ 6] = 0x00;
	state_frame[ 7] = 0x00; // led
	state_frame[ 8] = 0x00;
	state_frame[ 9] = 0xAC;
	state_frame[10] = 0x20;

#ifdef HID_DEBUG_MODE
	hid_raw_data[0][0] = 0;
	hid_raw_data[1][0] = 0;
#endif
}

void ch9350_rx_byte(unsigned char rx_data)
{
	// new byte received
	preamble  = (preamble << 8) | rx_data;
	preamble &= 0x00FFFFFF;

	switch (ch9350_status) {
		case STATE_IDLE:
			if ((preamble & 0x00FFFFF0) == 0x0057AB80) {
				if ((rx_data & 15) == 2)
					ch9350_status = STATE_REQ;
				else if ((rx_data & 15) == 8)
					ch9350_status = STATE_HID_LEN;
			}
			break;

		case STATE_REQ: {
				// TX 11 bytes of state frame
				int i;

				state_frame[7] = kbd_led_ctl;

				for (i = 0; i < 11; i++)
					ch9350_tx_byte(state_frame[i]);

				ch9350_status = STATE_IDLE;
			}
			break;

		case STATE_HID_LEN:
//			Sys_Uart_Put(UART0, '~');
			length = rx_data;
#ifdef HID_DEBUG_MODE
			if (payload_cnt < 250) {
				ch9350_status = STATE_DUMP_DAT;
				payload_cnt = 0;
			} else {
				ch9350_status = STATE_IDLE;
			}
#else
			if (length > 6) // 7, 9, 10, 11
				ch9350_status = STATE_DEV_ID;
			else
				ch9350_status = STATE_IDLE;
#endif
			break;

#ifdef HID_DEBUG_MODE
		case STATE_DUMP_DAT:
			if (0 == payload_cnt) {
				// dev id
				id = rx_data;
				hid_raw_data[id & 1][0] = length;
				payload_cnt = 1;
			}

			hid_raw_data[id & 1][payload_cnt] = rx_data;
			if (payload_cnt == length) {
				hid_raw_data[id & 1][0] |= 0x80; // end flag
				ch9350_status = STATE_IDLE;
			} else {
				payload_cnt++;
			}

			break;
#endif

		case STATE_DEV_ID:
//			Sys_Uart_Put(UART0, '!');
			id = rx_data;
			checksum = 0;
			phantom  = 0;
			payload_cnt = 0;
//			key_bitmap_new = 0;

#ifdef ENABLE_MOUSE
			mouse_key_new     = 0;
			mouse_delta_x_new = 0;
			mouse_delta_y_new = 0;
			mouse_wheel_new   = 0;
#endif

#ifdef ENABLE_JOYSTICK
			joy_status_new = 0;
#endif
			// id:
			// xxdd_xxxp
			// dd: 00 Unknown
			//     01 Keyboard
			//     10 Mouse
			//     11 Multimedia
			//  p:    Port num
			if ((rx_data & 0x30) == 0x30) {
				payload_cnt = 1;
				ch9350_status = STATE_MM_DAT;
			} else {
#ifdef ENABLE_JOYSTICK
				ch9350_status = (rx_data & 0x20) ? STATE_MOU_DATA :
								(rx_data & 0x10) ? STATE_KBD_DATA : STATE_JOY_DATA;
#else
				ch9350_status = STATE_KBD_DATA;
#endif
			}
			break;

		case STATE_MM_DAT:
			// just throw it
			payload_cnt++;
			if (payload_cnt == length) {
				ch9350_status = STATE_IDLE;
			}
			break;

		case STATE_KBD_DATA:
			if (payload_cnt == 0) {
				// modifier: E0-E7
				//key_bitmap_new[231:224] <= rx_data;
				memset(key_bitmap_new, 0, sizeof(key_bitmap_new));
				key_bitmap_new[28] = rx_data;
			} else if (payload_cnt > 1) { // skip reserved
				// for each key
				if (rx_data == 1) // phantom condition
					phantom = 1;

				if (rx_data) sysprintf("K:%02X\r\n", rx_data);

				//key_bitmap_new[rx_data] <= 1;
				key_bitmap_new[rx_data / 8] |= 1 << (rx_data & 7);
			}

			if (payload_cnt == 7)
				ch9350_status = STATE_KBD_SEQ;

			checksum = checksum + rx_data;
			payload_cnt = payload_cnt + 1;
			break;

		case STATE_KBD_SEQ:
//			if (seq_no + 1 != rx_data) // packet lost
//				;

			seq_no = rx_data;
			checksum = checksum + rx_data;
			ch9350_status = STATE_KBD_SUM;
			break;

		case STATE_KBD_SUM:
			if (checksum == rx_data && ~phantom) {
				// update key bitmap
				//key_bitmap <= key_bitmap_new;
				memcpy(key_bitmap, key_bitmap_new, sizeof(key_bitmap_new));
			}

			ch9350_status = STATE_IDLE;
			break;

#ifdef ENABLE_JOYSTICK
		case STATE_JOY_DATA:
//			Sys_Uart_Put(UART0, '@');
			// Right, Left, Down, Up, Start, Select, B, A
			if (payload_cnt == 0) {
				if (0xFF == rx_data) // FF Right
					joy_status_new |= 0x80;

				if (0x00 == rx_data) // 00 Left
					joy_status_new |= 0x40;
			} else if (payload_cnt == 1) {
				if (0xFF == rx_data) // FF Up
					joy_status_new |= 0x20;

				if (0x00 == rx_data) // 00 Down
					joy_status_new |= 0x10;
			} else if (payload_cnt == 5) {
				if (rx_data & 0xA0) // A: 20 or 80
					joy_status_new |= 1;

				if (rx_data & 0x50) // B: 10 or 40
					joy_status_new |= 2;
			} else if (payload_cnt == 6) {
				if (rx_data & 0x10) // Select
					joy_status_new |= 4;

				if (rx_data & 0x20) // Start
					joy_status_new |= 8;
			}

			if (payload_cnt == 7)
				ch9350_status = STATE_JOY_SEQ;

			checksum = checksum + rx_data;
			payload_cnt = payload_cnt + 1;
			break;

		case STATE_JOY_SEQ:
//			Sys_Uart_Put(UART0, '#');
//			if (seq_no + 1 != rx_data) // packet lost
//				;
			seq_no = rx_data;
			checksum = checksum + rx_data;
			ch9350_status = STATE_JOY_SUM;
			break;

		case STATE_JOY_SUM:
			if (checksum == rx_data) {
				// update joy status
//				Sys_Uart_Put(UART0, '$');
				if (id & 1) {
					mouse_present = 0;
					joy2_status = joy_status_new;
				} else {
					joy1_status = joy_status_new;
				}

				unsigned char changed  =  joy_status_new ^ joy_status_old;
				unsigned char pressed  =  joy_status_new & changed;
				unsigned char released = ~joy_status_new & changed;

				if (pressed) sysprintf("P:%02X\r\n", pressed);
				if (released) sysprintf("R:%02X\r\n", released);

				joy_status_old = joy_status_new;
			}

			ch9350_status = STATE_IDLE;
			break;
#endif

#ifdef ENABLE_MOUSE
		case STATE_MOU_DATA:
			if (length == 11) {
				if (0 == emu_mini_kbd) {
					// 16 bits mode with prefix & suffix
					if (payload_cnt == 1) {
						mouse_key_new = rx_data & 7;
					} else if (payload_cnt == 2) {
						mouse_delta_x_new = rx_data;
					} else if (payload_cnt == 3) {
						mouse_delta_x_new |= rx_data << 8;
					} else if (payload_cnt == 4) {
						mouse_delta_y_new = rx_data;
					} else if (payload_cnt == 5) {
						mouse_delta_y_new |= rx_data << 8;
					} else if (payload_cnt == 6) {
						mouse_wheel_new = rx_data;
					} else if (payload_cnt == 7) {
						ch9350_status = STATE_MOU_SEQ;
					}
				} else {
					// Rii mini X1 keyboard use this pattern
					if (payload_cnt == 1) {
						mouse_key_new = rx_data & 7;
					} else if (payload_cnt == 2) {
						short d = rx_data << 8;
						mouse_delta_x_new = d >> 8;
					} else if (payload_cnt == 3) {
						short d = rx_data << 8;
						mouse_delta_y_new = d >> 8;
					} else if (payload_cnt == 4) {
						mouse_wheel_new = rx_data;
					} else if (payload_cnt == 7) {
						ch9350_status = STATE_MOU_SEQ;
					}
				}
			} else if (length == 10) {
				// 12 bits mode with prefix & suffix
				if (payload_cnt == 1) {
					mouse_key_new = rx_data & 7;
				} else if (payload_cnt == 2) {
					mouse_delta_x_new = rx_data;
				} else if (payload_cnt == 3) {
					short d = rx_data << 12;
					mouse_delta_x_new |= d >> 4;
					mouse_delta_y_new = rx_data >> 4;
				} else if (payload_cnt == 4) {
					short d = rx_data << 8;
					mouse_delta_y_new |= d >> 4;
				} else if (payload_cnt == 5) {
					mouse_wheel_new = rx_data;
				} else if (payload_cnt == 6) {
					ch9350_status = STATE_MOU_SEQ;
				}
			} else if (length == 9) {
				// 16 bits mode without prefix or suffix
				if (payload_cnt == 0) {
					mouse_key_new = rx_data & 7;
				} else if (payload_cnt == 1) {
					mouse_delta_x_new = rx_data;
				} else if (payload_cnt == 2) {
					mouse_delta_x_new = rx_data << 8;
				} else if (payload_cnt == 3) {
					mouse_delta_y_new = rx_data;
				} else if (payload_cnt == 4) {
					mouse_delta_y_new = rx_data << 8;
				} else if (payload_cnt == 5) {
					mouse_wheel_new = rx_data;
					ch9350_status = STATE_MOU_SEQ;
				}
			} else if (length == 8) {
				if (payload_cnt == 1) {
					mouse_key_new = rx_data & 7;
				} else if (payload_cnt == 2) {
					short d = rx_data << 8;
					mouse_delta_x_new = d >> 8;
				} else if (payload_cnt == 3) {
					short d = rx_data << 8;
					mouse_delta_y_new = d >> 8;
				} else if (payload_cnt == 4) {
					mouse_wheel_new = rx_data;
					ch9350_status = STATE_MOU_SEQ;
				}
			} else if (length == 7) {
				// 8 bits mode without prefix or suffix
				if (payload_cnt == 0) {
					mouse_key_new = rx_data & 7;
				} else if (payload_cnt == 1) {
					short d = rx_data << 8;
					mouse_delta_x_new = d >> 8;
				} else if (payload_cnt == 2) {
					short d = rx_data << 8;
					mouse_delta_y_new = d >> 8;
				} else if (payload_cnt == 3) {
					mouse_wheel_new = rx_data;
					ch9350_status = STATE_MOU_SEQ;
				}
			}

			checksum = checksum + rx_data;
			payload_cnt = payload_cnt + 1;
			break;

		case STATE_MOU_SEQ:
//			if (seq_no + 1 != rx_data) // packet lost
//				;
			seq_no = rx_data;
			checksum = checksum + rx_data;
			ch9350_status = STATE_MOU_SUM;
			break;

		case STATE_MOU_SUM:
			if (checksum == rx_data) {
				// update mouse status
				mouse_present = 1;
				mouse_key     = mouse_key_new;
				mouse_wheel   = mouse_wheel_new;
#ifdef MOUSE_SLOWDOWN
				// speed / 2
				mouse_delta_x = mouse_delta_x_new >> 1;
				mouse_delta_y = mouse_delta_y_new >> 1;
#else
				mouse_delta_x = mouse_delta_x_new;
				mouse_delta_y = mouse_delta_y_new;
#endif
				mouse_delta_x_gui = mouse_delta_x_new;
				mouse_delta_y_gui = mouse_delta_y_new;

			}

			ch9350_status = STATE_IDLE;
			break;
#endif

		default:
			ch9350_status = STATE_IDLE;
			break;
	}
}
